package cn.xinfei.xdecision.engine.runner.executor.strategy.node;

import cn.xinfei.xdecision.common.model.component.scorecard.ScorecardDetail;
import cn.xinfei.xdecision.common.model.component.scorecard.ScorecardDimension;
import cn.xinfei.xdecision.common.model.component.scorecard.vo.ScorecardVersionVo;
import cn.xinfei.xdecision.common.model.component.scorecard.vo.ScorecardVo;
import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.enginex.model.EngineNodeModel;
import cn.xinfei.xdecision.common.model.enginex.model.NodeJsonModel;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.component.scorecard.ScorecardDetailService;
import cn.xinfei.xdecision.common.service.component.scorecard.ScorecardDimensionService;
import cn.xinfei.xdecision.common.service.component.scorecard.ScorecardService;
import cn.xinfei.xdecision.common.service.component.scorecard.ScorecardVersionService;
import cn.xinfei.xdecision.common.service.datax.datamanage.FieldService;
import cn.xinfei.xdecision.common.utils.enums.enginex.ConditionValueTypeEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.XRunner;
import cn.xinfei.xdecision.engine.runner.executor.handler.ScorecardNodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import cn.xinfei.xdecision.engine.runner.executor.strategy.EngineNodeRunner;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.constant.EngineResultEnum.EXCEPTION_NODE_NULL_END;


@XRunner
@Slf4j
@Service
public class Scorecard extends EngineNodeRunner {

    @Autowired
    private ScorecardNodeHandler scorecardNodeHandler;

    @Resource
    public FieldService fieldService;

    @Resource
    public ScorecardService scorecardService;

    @Autowired
    private ScorecardDimensionService scorecardDimensionService;  // 维度

    @Autowired
    private ScorecardDetailService scorecardDetailService;  // 明细

    @Autowired
    private ScorecardVersionService scorecardVersionService;

    private static final String SCORE_CARD_CACHE_PRE = "score_card_cache_pre_";

    @Override
    public String getServiceName() {
        return EngineNodeRunner.PRE + NodeTypeEnum.SCORECARD.getType();
    }

    @Override
    public void before(NodeHandler nodeHandler) {

    }

    @Override
    public Map<String, FieldInfoVo> getInputFieldInfo(NodeHandler nodeHandler) {

        Map<String, FieldInfoVo> fieldVos = super.getInputFieldInfo(nodeHandler);
        try {
            EngineNode engineNode = nodeHandler.getEngineNode();
            String nodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeJson)) {
                return fieldVos;
            }
            Set<String> fieldCodes = new HashSet<>();
            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeJson, NodeJsonModel.class);
            List<EngineNodeModel> scorecardList = nodeJsonModel.getComponentList();
            if (null != scorecardList && scorecardList.size() > 0) {
                scorecardList.forEach(decisionTable -> {
                    Long versionId = decisionTable.getVersionId();
                    List<ScorecardDimension> scorecardDimensions = scorecardDimensionService.getDimensionListByVersionId(versionId);
                    List<Integer> dimensionIds = scorecardDimensions.stream().map(item -> item.getId()).collect(Collectors.toList());
                    List<ScorecardDetail> scorecardDetails = scorecardDetailService.getDetailListByDimensionIds(dimensionIds);
                    fieldCodes.addAll(scorecardDetails.stream().map(item -> item.getFieldCode()).collect(Collectors.toSet()));

                    ScorecardVo scorecard = scorecardService.queryExecuteScorecard(versionId);
                    nodeHandler.put(SCORE_CARD_CACHE_PRE + versionId, scorecard);
                });
            }
            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();

            fieldCodes.forEach(fieldCode -> {
                FieldInfoVo value = fieldInfo.get(fieldCode);
                if (value != null) {
                    fieldVos.put(fieldCode, value);
                }
            });

        } catch (Exception e) {
            log.error("ScorecardNode,getInputFieldInfo:error={}", e.getMessage(), e);
        }
        return fieldVos;

    }

    /**
     * 评分卡自有固定输出
     * 解析出入参的方法其实可以合并，减少对数据库的访问
     *
     * @param nodeHandler
     * @return
     */
    @Override
    public Map<String, StrategyOutput> getOutputFieldInfo(NodeHandler nodeHandler) {
        Map<String, StrategyOutput> strategyOutputMap = super.getOutputFieldInfo(nodeHandler);
        EngineNode engineNode = nodeHandler.getEngineNode();
        try {
            String nodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeJson)) {
                log.error("Scorecard node json is null");
                return strategyOutputMap;
            }

            Set<String> fieldCodes = new HashSet<>();
            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeJson, NodeJsonModel.class);
            List<EngineNodeModel> scorecardList = nodeJsonModel.getComponentList();
            if (null != scorecardList && scorecardList.size() > 0) {

                Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
                scorecardList.forEach(decisionTable -> {
                    String scorecardCode = decisionTable.getCode();
                    List<ScorecardVersionVo> scorecardVersionVoList = scorecardVersionService.queryVersionListByScorecardId(scorecardCode, decisionTable.getVersionNo());

                    for (ScorecardVersionVo versionVo : scorecardVersionVoList) {
                        String resultFieldCode = versionVo.getResultFieldCode();
                        FieldInfoVo value = fieldInfo.get(resultFieldCode);
                        if (value != null) {
                            StrategyOutput output = new StrategyOutput();
                            output.setFieldCode(resultFieldCode);
                            output.setVarValueType(value.getValueType());
                            //固定输出， 此时ValueType并不一定会在后续过程中起作用，一定会在runNode中给他负值，但会存在赋值时报错的可能，比如类型不匹配，所以设置为常量，在值缺失的时候取默认值
                            //评分科区别于决策树和决策表，没有默认值，所以去
                            output.setValueType(ConditionValueTypeEnum.VARIABLE.getCode());
                            output.setFieldValue(value.getVarDefaultValue());
                            output.setStage(value.getStage());
                            output.setFixed(true);
                            strategyOutputMap.put(resultFieldCode, output);
                        } else {
                            log.warn("变量不存在,fieldCode={}", resultFieldCode);
                        }
                    }
                });
            }


        } catch (Exception e) {
            log.error("ScorecardNode,getOutputFieldInfo:error={}", e.getMessage(), e);
        }
        return strategyOutputMap;
    }


    @Override
    public StateTransmitModel runNode(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();

        NodeJsonModel nodeJsonModel = JSONObject.parseObject(engineNode.getNodeJson(), NodeJsonModel.class);
        if (null == nodeJsonModel) {
            log.error("Scorecard node json is null");
            return XDecisionContext.getContext().getStateContext().setEngineResultEnum(EXCEPTION_NODE_NULL_END);
        }
        List<EngineNodeModel> scorecardList = nodeJsonModel.getComponentList();
        Set<Long> versionIds = scorecardList.stream().map(decisionTable -> {
            Long versionId = decisionTable.getVersionId();
            return versionId;
        }).collect(Collectors.toSet());

        /**
         * 评分卡节点只能选择一个评分卡实例
         * versionIds.size=1
         */
        String nextNodeCode = nodeHandler.getEngineNode().getNextNodeCodes();
        for (Long versionId : versionIds) {
            //从缓存中取
            ScorecardVo scorecard = (ScorecardVo) nodeHandler.get(SCORE_CARD_CACHE_PRE + versionId);
            if (scorecard == null) {
                log.warn("评分卡没有可用的版本");
                continue;
            }

            ScorecardVersionVo versionVo = scorecard.getExecuteVersion();
            BigDecimal totalScore = scorecardNodeHandler.executeDecisionTree(versionVo);

            String resultFieldEn = versionVo.getResultFieldCode();
            if (null != totalScore) {
                setOutput(nodeHandler, resultFieldEn, totalScore);
            }

            String nodeCode = engineNode.getNodeCode();
            log.info("评分卡执行结果，nodeCode={},{}={}", nodeCode, resultFieldEn, totalScore);

        }
        return XDecisionContext.getContext().getStateContext().setNextNodeCode(nextNodeCode);
    }
}
